﻿#include "Spaceship.h"
#include "Coordinate.h"
#include "Background.h"

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

#include <memory>

#pragma region FunctionDeclaration
// 函数声明
bool Initialize(HWND hWnd);
bool InitObjects();
void RenderScene(DWORD red, DWORD green, DWORD blue);
void Shutdown();

#pragma endregion

#pragma region GlobalVariable
// 全局变量

wrl::ComPtr<IDirect3D9> g_Direct3D = nullptr;
wrl::ComPtr<IDirect3DDevice9> g_Device = nullptr;

std::unique_ptr<Spaceship> g_Spaceship = nullptr;
std::unique_ptr<Coordinate> g_Coordinate = nullptr;
std::unique_ptr<Background> g_Background = nullptr;

const int WindowWidth = 800;
const int WindowHeight = 600;

#define ClassName "day day up"
#define WindowName "爱白菜的小昆虫"

#pragma endregion

LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    case WM_KEYUP:
        if (wParam == VK_ESCAPE) PostQuitMessage(0);
        break;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}

int APIENTRY wWinMain(
    _In_ HINSTANCE,
    _In_opt_ HINSTANCE,
    _In_ LPWSTR,
    _In_ INT)
{
    // 1.定义窗口类
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(wcex);//指定结构体的大小
    wcex.style = CS_CLASSDC;//结构体的大小，‌用于版本控制。‌
    wcex.lpfnWndProc = MsgProc;//指向窗口过程的指针，‌处理窗口的各种消息。‌
    wcex.cbClsExtra = 0;//窗口类附加内存的大小，‌通常设为0。‌
    wcex.cbWndExtra = 0;//窗口附加内存的大小，‌通常设为0。‌
    wcex.hInstance = GetModuleHandle(NULL);//应用程序实例句柄
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);//窗口类的光标句柄
    //wcex.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(100, 100, 100));//窗口背景画刷句柄
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);  // 使用默认的背景颜色
    wcex.lpszMenuName = nullptr;//窗口类的菜单
    wcex.lpszClassName = ClassName;//窗口类名
    wcex.hIcon = nullptr;//类图标的句柄
    wcex.hIconSm = nullptr;//小图标的句柄

    // 2.注册窗口类
    RegisterClassEx(&wcex);//注册本地窗口

    RECT rect;
    rect.left = rect.top = 0;
    rect.right = WindowWidth;
    rect.bottom = WindowHeight;
    AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);//根据所需的客户端矩形大小计算窗口矩形的所需大小

    INT cx = GetSystemMetrics(SM_CXSCREEN);
    INT cy = GetSystemMetrics(SM_CYSCREEN);
    // 3.创建窗口
    HWND hWnd = CreateWindowEx(
        0,//扩展窗口样式
        ClassName,//窗口类名或窗口类原子的指针
        WindowName,//窗口名
        WS_OVERLAPPEDWINDOW,//窗口样式
        (cx - WindowWidth) / 2,//显示窗口位置
        (cy - WindowHeight) / 2,
        rect.right - rect.left,//创建窗口大小
        rect.bottom - rect.top,
        HWND_DESKTOP,//没有父窗口
        nullptr,//菜单句柄或子窗口ID
        wcex.hInstance,//应用程序实例句柄
        nullptr//指向窗口创建数据的指针
    );

    if (nullptr == hWnd)
        return -1;

    // 初始化 Direct3D
    if (Initialize(hWnd)) {
        // 4.显示窗口
        ShowWindow(hWnd, SW_SHOW);//设置指定窗口的显示状态
        UpdateWindow(hWnd);

        MSG msg{ 0 };
        while (msg.message != WM_QUIT) {
            if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else {
                RenderScene(100, 100, 100);
            }
        }
    }

    // 释放资源
    Shutdown();

    // 注销windows
    UnregisterClass(ClassName, wcex.hInstance);
    return 0;
}


bool Initialize(HWND hWnd)
{
    HRESULT hresult{ };
    // 创建Direct3D接口对象
    g_Direct3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (nullptr == g_Direct3D)
        return false;

    D3DDISPLAYMODE displayMode{ };
    // 获得当前显卡的显示模式
    hresult = g_Direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode);
    if (FAILED(hresult))
        return false;

    D3DCAPS9 caps{ };
    // 
    hresult = g_Direct3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
    if (FAILED(hresult))
        return false;

    DWORD processing{ };
    if (caps.VertexProcessingCaps != 0) {
        // 使用硬件来处理顶点数据
        processing = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE;
    }
    else {
        // 使用软件来处理顶点数据
        processing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
    }

    // 定义Direct3D窗口的显示信息
    D3DPRESENT_PARAMETERS d3dpp{ 0 };

    d3dpp.BackBufferWidth = WindowWidth;// 后台缓冲区宽度
    d3dpp.BackBufferHeight = WindowHeight;// 后台缓冲区高度
    d3dpp.BackBufferFormat = displayMode.Format;// 后台缓冲区格式
    d3dpp.BackBufferCount = 1;// 后台缓冲区数量
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;// 交换效果
    d3dpp.hDeviceWindow = hWnd;// 关联的窗口句柄
    d3dpp.Windowed = TRUE;// TRUE：窗口模式，FALSE全屏模式
    d3dpp.EnableAutoDepthStencil = TRUE;// TRUE：启动自动深度模版缓冲区
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;// 自动深度模版缓冲区格式
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;// 运行时会立即更新窗口工作区

    // 创建设备
    hresult = g_Direct3D->CreateDevice(
        D3DADAPTER_DEFAULT,//指定正在使用的显卡
        D3DDEVTYPE_HAL,//硬件抽象层设备类型
        hWnd,//窗口句柄
        processing,
        &d3dpp,
        &g_Device
    );
    if (FAILED(hresult))
        return false;

    if (!InitObjects())
        return false;

    return true;
}

bool InitObjects()
{
    D3DXMATRIX matProj;
    D3DXMatrixOrthoLH(&matProj, WindowWidth, WindowHeight, 0.1f, 1000.0f);
    g_Device->SetTransform(D3DTS_PROJECTION, &matProj);

    g_Device->SetRenderState(D3DRS_LIGHTING, FALSE);
    g_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

    g_Spaceship = std::make_unique<Spaceship>(g_Device.Get());
    g_Coordinate = std::make_unique<Coordinate>(g_Device.Get(), (float)WindowWidth, (float)WindowHeight);
    g_Background = std::make_unique<Background>(g_Device.Get(), (float)WindowWidth, (float)WindowHeight);

    return true;
}


void RenderScene(DWORD red, DWORD green, DWORD blue)
{
    // 清理后台缓冲
    g_Device->Clear(0, nullptr, D3DCLEAR_TARGET, D3DCOLOR_XRGB(red, green, blue), 1.0f, 0);
    // 场景开始渲染
    g_Device->BeginScene();

    g_Background->Update();
    g_Background->Draw(g_Device.Get());

    g_Spaceship->Update();
    g_Spaceship->Draw(g_Device.Get());

    g_Coordinate->Update();
    g_Coordinate->Draw(g_Device.Get());

    // 场景结束渲染
    g_Device->EndScene();
    // 显示场景
    g_Device->Present(nullptr, nullptr, nullptr, nullptr);
}


void Shutdown()
{

}